using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

namespace BepuUtilities.Memory
{
    /// <summary>
    /// 管理一种资源类型的缓存。
    /// </summary>
    /// <typeparam name="T">要池的对象类型。</typeparam>
    public class Pool<T>
    {
        Stack<T> stack = new Stack<T>();

        /// <summary>
        /// 获取池中现有元素的数量。可以在不创建任何新元素的情况下请求此数量的元素。
        /// </summary>
        /// <remarks> 
        /// 不执行任何锁定。当元素可能仍在池中添加或删除时,不应使用。
        /// </remarks>
        public int PooledElementCount
        {
            get
            {
                return stack.Count;
            }
        }

        /// <summary>
        /// 获取或设置当池中没有可用的现有对象时用于创建新对象的函数。
        /// </summary>
        public Func<T> Creator
        {
            get; set;
        }

        /// <summary>
        /// 获取或设置用于初始化从池中获取的对象的函数。即使对象刚由Creator委托创建,也会运行。
        /// </summary>
        public Action<T> Initializer
        {
            get; set;
        }

        /// <summary>
        /// 获取或设置元素返回池时应用于该元素的操作。
        /// </summary>
        public Action<T> Cleaner
        {
            get; set;
        }

        public Pool(Func<T> creator, Action<T> initializer = null, Action<T> cleaner = null)
        {
            Creator = creator ?? throw new ArgumentException("Creator must not be null.");
            Initializer = initializer;
            Cleaner = cleaner;
        }

#if DEBUG
        public HashSet<T> OutstandingElements = new HashSet<T>();
#endif

        /// <summary>
        /// 从池中清除所有元素。
        /// </summary>
        public void Clear()
        {
            stack.Clear();
        }

        [Conditional("DEBUG")]
        public void CheckIfOutstandingElementsExist()
        {
#if DEBUG
            Debug.Assert(OutstandingElements.Count == 0);
#endif
        }

        /// <summary>
        /// 从池中提取元素。如果池为空,则创建并返回新资源。
        /// </summary>
        /// <return>池中的元素。</return>
        public T Take()
        {
            T item;
            if (stack.Count > 0)
            {
                item = stack.Pop();
            }
            else
            {
                item = Creator();
            }
            Initializer?.Invoke(item);
#if DEBUG
            OutstandingElements.Add(item);
#endif
            return item;
        }


        /// <summary>
        /// 将指定项返回到池中。如果设置了较干净的委托,则会清除该项。
        /// </summary>
        /// <param name="Item">要回馈给池的项目。</param>
        public void Return(T item)
        {
#if DEBUG
            if (!OutstandingElements.Remove(item))
                throw new InvalidOperationException("Cannot return an item that did not originate from this pool.");
#endif

            Cleaner?.Invoke(item);

            stack.Push(item);
        }
    }
}
